using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

using VistaDB.DDA;
using VistaDB.Provider;

using NSimpleDB.Service.Contract;
using NSimpleDB.Service.VistaDb.Managers;

namespace NSimpleDB.Service.VistaDb.Managers
{
    internal class ItemManager
    {
        private SchemaManager schemaMang;
        private DomainManager domainMang;

        public ItemManager(SchemaManager schemaMang, DomainManager domainMang)
        {
            this.schemaMang = schemaMang;
            this.domainMang = domainMang;
        }


        public void PutAttributes(string domainName, string itemName, params ISimpleDBAttribute[] attributes)
        {
            int idDomain = this.domainMang.GetDomainIdFromName(domainName);
            if (idDomain < 0) throw new SimpleDBException("PutAttributes: Unknown domain name '{0}'!", domainName);

            using (IVistaDBTable tbD = this.schemaMang.OpenDomainTable(idDomain))
            {
                foreach (ISimpleDBAttribute a in attributes)
                {
                    ISimpleDBAttributeToPut aPut = a as ISimpleDBAttributeToPut;

                    try
                    {
                        if (aPut != null && aPut.Replace)
                        {
                            // first delete all values of given attribute
                            tbD.ActiveIndex = "PK";
                            IVistaDBRow key = tbD.CurrentKey;
                            key.InitTop();
                            key["itemName"].Value = itemName;
                            key["attributeName"].Value = aPut.Name;
                            if (tbD.Find(key, "PK", true, false))
                            {
                                // del all attributes of given name belonging to given item
                                do
                                {
                                    tbD.Delete(); // delete also advances the cursor
                                } while (!tbD.EndOfTable &&
                                         tbD.Get("itemName").Value.ToString() == itemName &&
                                         tbD.Get("attributeName").Value.ToString() == aPut.Name);
                            }
                        }

                        // add new attribute value to item
                        tbD.Insert();
                        tbD.PutString("itemName", itemName);
                        tbD.PutString("attributeName", a.Name);
                        tbD.PutString("attributeValue", a.Value);
                        tbD.Post();
                    }
                    catch (VistaDB.Diagnostic.VistaDBException ex)
                    {
                        // ignore if duplicate attribute/value pairs are created
                        if (ex.ErrorId != 250)
                            throw ex;
                    }
                }
            }
        }


        public DataTable GetAttributes(string domainName, string itemName, params string[] attributeNames)
        {
            int idDomain = this.domainMang.GetDomainIdFromName(domainName);
            if (idDomain < 0) throw new SimpleDBException("GetAttributes: Unknown domain name '{0}'!", domainName);

            using (VistaDBConnection conn = new VistaDBConnection(this.schemaMang.Database))
            {
                List<ISimpleDBAttribute> selectedAttributes = new List<ISimpleDBAttribute>();

                // build select statement for attributes
                List<ISimpleDBAttribute> queryAttributes = new List<ISimpleDBAttribute>();
                foreach (string attributeName in attributeNames)
                    queryAttributes.Add(new SimpleDBAttribute(attributeName, ""));
                string where = this.CompileAttributesToItemSelectSqlWhere(itemName, queryAttributes.ToArray(), false);
                string sql = string.Format("select attributeName, attributeValue from {0} where {1} order by attributeName, attributeValue", this.schemaMang.DomainTableNameFromId(idDomain), where);

                // select attributes of item
                VistaDBDataAdapter adap = new VistaDBDataAdapter(sql, conn);
                DataTable tbResultset = new DataTable("Attributes");
                adap.Fill(tbResultset);

                return tbResultset;
            }
        }


        public void DeleteAttributes(string domainName, string itemName, ISimpleDBAttribute[] attributes, bool includeAttributes)
        {
            int idDomain = this.domainMang.GetDomainIdFromName(domainName);
            if (idDomain < 0) throw new SimpleDBException("DeleteAttributes: Unknown domain name '{0}'!", domainName);

            using (VistaDBConnection conn = new VistaDBConnection(this.schemaMang.Database))
            {
                conn.Open();

                string where = this.CompileAttributesToItemSelectSqlWhere(itemName, attributes, includeAttributes);
                string sql = string.Format("delete from {0} where {1}", this.schemaMang.DomainTableNameFromId(idDomain), where);
                VistaDBCommand cmdDel = new VistaDBCommand(sql, conn);
                cmdDel.ExecuteNonQuery();
            }
        }


        internal string CompileAttributesToItemSelectSqlWhere(string itemId, ISimpleDBAttribute[] attributes, bool includeValues)
        {
            // build where clause like this:
            // itemName='id' and ((attributeName='name') or (...) or ...)
            //      or
            // itemName='id' and ((attributeName='name' and attributeValue='value') or (...) or ...)
            StringBuilder where = new StringBuilder(string.Format("itemName='{0}'", itemId));
            if (attributes.Length > 0)
            {
                where.Append(" and (");

                for (int i = 0; i < attributes.Length; i++)
                {
                    if (i > 0) where.Append(" or ");
                    where.Append("(");
                    where.Append(string.Format("attributeName='{0}'", attributes[i].Name));
                    if (includeValues)
                        where.Append(string.Format(" and attributeValue='{0}'", attributes[i].Value));
                    where.Append(")");
                }

                where.Append(")");
            }
            return where.ToString();
        }
    }
}
